const acorn = require('acorn');
const jsx = require('acorn-jsx');
const https = require('https');
const fs = require('fs');
const path = require('path');
// 配置支持JSX和最新语法
const parser = acorn.Parser.extend(jsx());
// 特性检测规则映射表
const FEATURE_DETECTORS = {
  // 1. let/const
  'let/const': node => 
    node.type === 'VariableDeclaration' && 
    ['let', 'const'].includes(node.kind),
  // 2. 解构赋值 (对象和数组)
  '解构赋值': node => 
    (node.type === 'VariableDeclarator' &&  node.id &&
     (node.id.type === 'ObjectPattern' || node.id.type === 'ArrayPattern')) ||
    (node.type === 'AssignmentPattern' &&  node.left &&
     (node.left.type === 'ObjectPattern' || node.left.type === 'ArrayPattern')),
  // 3. 参数处理
  '默认参数': node => 
  node.type === 'AssignmentPattern' && 
  (node.parent && node.parent.type === 'FunctionParameter'),
    '剩余参数': node => 
    node.type === 'RestElement' && 
    (node.parent && node.parent.type === 'FunctionParameter'),  
  // 4. 生成器函数
  '生成器': node =>
    (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') && 
    node.generator === true,
  // 5. raw标签函数
  'raw标签函数': node =>
    node.type === 'TaggedTemplateExpression' && node.tag &&
    node.tag.type === 'MemberExpression' &&
    node.tag.object?.name === 'String' &&
    node.tag.property?.name === 'raw',
  // 6. 对象特性
  '对象简写': node => 
    node.type === 'Property' && 
    node.shorthand === true,
  '计算属性名': node =>
    node.type === 'Property' && 
    node.computed === true,
  // 7. 合并语法
  '合并语法': node =>
    node.type === 'SpreadElement' && node.parent &&
    (node.parent.type === 'ArrayExpression' || node.parent.type === 'ObjectExpression'),
  // 8. 类与继承
  '类定义': node =>
    node.type === 'ClassDeclaration' || 
    node.type === 'ClassExpression',
  '类继承': node =>
    (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') &&
    node.superClass !== null,
  // 9. 模块系统
  '动态导入': node => node.type === 'ImportExpression',
  '模块导入': node => node.type === 'ImportDeclaration',
  '默认导出': node => node.type === 'ExportDefaultDeclaration',
  '命名导出': node => node.type === 'ExportNamedDeclaration',
  //10.eval与with
  'eval调用': node => node.type === 'CallExpression' && node.callee &&
  node.callee.type === 'Identifier' && node.callee.name === 'eval',
  'with语句': node => node.type === 'WithStatement'
};

// 新增日志文件路径
const ERROR_LOG = path.join(__dirname, 'error_urls.txt');
const ES6_LOG = path.join(__dirname, 'es6_urls.txt');
async function initLogFiles() {
  // 清空或创建日志文件
  await Promise.all([
    fs.promises.writeFile(ERROR_LOG, ''),
    fs.promises.writeFile(ES6_LOG, '')
  ]);
}

const zlib = require('zlib');

// 增强版抓取函数
async function fetchJS(url) {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      if (res.statusCode !== 200) {
        fs.promises.appendFile(ERROR_LOG, `${url}\n`).catch(() => {});  // 记录错误URL
        return reject(new Error(`HTTP ${res.statusCode}`));
      }

      let data = '';

      // 检查 Content-Encoding 是否为 gzip
      if (res.headers['content-encoding'] === 'gzip') {
        res.pipe(zlib.createGunzip()).on('data', (chunk) => {
          data += chunk;
        }).on('end', () => {
          console.log(`Fetched and decompressed data with length: ${data.length} characters`);
          resolve(data);
        }).on('error', reject);
      } else {
        res.on('data', (chunk) => {
          data += chunk;
        });
        res.on('end', () => {
          console.log(`Fetched data with length: ${data.length} characters`);
          resolve(data);
        });
      }
    }).on('error', async (err) => {
      await fs.promises.appendFile(ERROR_LOG, `${url}\n`).catch(() => {});  // 记录网络错误
      reject(err);
    });
  });
}





// AST遍历检测器
function detectFeatures(ast) {
  const detected = new Set();
  function traverse(node) {
    // 避免空节点
    if (node == null) {
      return;
    }
    // 检查所有检测规则
    for (const [feature, detector] of Object.entries(FEATURE_DETECTORS)) {
      if (detector(node)) detected.add(feature);
    }
    // 递归遍历子节点
    for (const key in node) {
      const child = node[key];
      if (child && typeof child === 'object') {
        if (Array.isArray(child)) {
          child.forEach(traverse);
        } else if (child.type) {
          traverse(child);
        }
      }
    }
  }
  traverse(ast);
  return detected;
}

const crypto = require('crypto');

function generateFileName(url) {
  // 使用 SHA256 哈希算法生成文件名
  const hash = crypto.createHash('sha256').update(url).digest('hex');
  return `${hash}`;
}
// 记录错误
async function logError(url, message) {
  await fs.promises.appendFile(ERROR_LOG, `${url} - Error: ${message}\n`).catch(() => {});
}
// 保存ES6特性为TXT文件
async function saveFeatures(url, features, outputDir) {
  const fileName = `${generateFileName(url)}-features.txt`;
  const filePath = path.join(outputDir, fileName);
  const content = `Detected ES6+ features for ${path.basename(url)}:\n` +
    features.map(f => `► ${f}`).join('\n');
  await fs.promises.writeFile(filePath, content, 'utf8');
  console.log(`Features saved to ${filePath}`);
}

function customJSONStringify(obj) {
  return JSON.stringify(obj, (key, value) => {
    if (typeof value === 'bigint') {
      // 将 BigInt 转换为字符串
      return value.toString();
    }
    return value;
  });
}

// 保存AST为JSON文件
async function saveAST(url, ast, outputDir) {
  const fileName = `${generateFileName(url)}.json`;
  const filePath = path.join(outputDir, fileName);
  // 使用自定义的 JSON.stringify 方法
  const astString = customJSONStringify(ast);
  await fs.promises.writeFile(filePath, astString, 'utf8');
  console.log(`AST saved to ${filePath}`);
}

// 修复代码中的 with 语句
function fixWithStatements(code) {
  let fixedCode = code;
  let numFixed = 0;
  const withRegex = /delete/;

  // 逐步修复，直到没有更多的 with 语句
  while (withRegex.test(fixedCode)) {
   // console.log(numFixed);
    fixedCode = fixedCode.replace(withRegex, '');
    numFixed++;
  }

  if (numFixed > 0) {
    console.log(`Fixed ${numFixed} 'delete' statements in the code.`);
  }

  return fixedCode;
}


// 尝试解析代码并修复 with 语句
async function fixAndParseCode(fixedCode, url) {
  let maxAttempts = 10; // 防止无限循环
  while (maxAttempts > 0) {
    try {
      const ast = parser.parse(fixedCode, { ecmaVersion: 'latest', sourceType: 'module' });
      return ast; // 成功解析，返回 AST
    } catch (parseError) {
      if (parseError.message.includes("Deleting local variable in strict mode")) {
        await logError(url, `Parse Error: ${parseError.message}`);
        //console.log(-1);
        // 尝试修复代码，删除 with 语句
        fixedCode = fixWithStatements(fixedCode);
      } else {
        throw parseError; // 重新抛出其他类型的解析错误
      }
      maxAttempts--; // 减少尝试次数
    }
  }
  throw new Error(`Failed to parse code after ${10} attempts`);
}

// 处理单个URL
async function processURL(url, outputDir) {
  try {
    const code = await fetchJS(url);
    let fixedCode = code;

    const ast = await fixAndParseCode(fixedCode, url);

    await saveAST(url, ast, outputDir);
    const features = detectFeatures(ast);

    if (features.size > 0) {
      // 记录包含ES6特性的URL及其对应的特性
      const featuresArray = Array.from(features).sort();
      const featuresString = featuresArray.join(', ');
      await fs.promises.appendFile(ES6_LOG, `${url} - Features: ${featuresString}\n`);
    }

    await saveFeatures(url, Array.from(features).sort(), outputDir);
  } catch (error) {
    console.error(`Error processing URL ${url}:`, error);
    await logError(url, `Processing Error: ${error.message}`);
  }
}


  // 读取txt文件中的URL列表
function readUrlsFromTxt(filePath) {
  const urls = fs.readFileSync(filePath, 'utf-8').split('\n').map(url => url.trim()).filter(url => url);
  return urls;
}


// 读取URLs文件并处理每个URL
async function processURLsFile(urlsFilePath, outputDir) {
  try {

    await initLogFiles();
    // 读取文件内容
    const urls = readUrlsFromTxt(urlsFilePath);
    
    // 检查输出目录是否存在，如果不存在则创建
    if (!await fs.promises.access(outputDir)) {
      await fs.promises.mkdir(outputDir, { recursive: true });
    }

    // 处理每个URL
    for (const url of urls) {
      if (url) { // 忽略空行
        await processURL(url, outputDir);
      }
    }
  } catch (error) {
    console.error(`Error reading URLs file ${urlsFilePath}:`, error);
    
  }
}

  // 使用示例
  const urlsFilePath = 'es6/test.txt'; // 替换为您的URLs文件路径
  const outputDir = 'es6/output'; // 替换为您希望保存输出的目录
  processURLsFile(urlsFilePath, outputDir);